package com.google.code.pokerstats_service.math;

import edu.ucla.stat.SOCR.analyses.exception.DataIsEmptyException;
import edu.ucla.stat.SOCR.util.AnalysisUtility;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.code.pokerstats_service.Game;
import com.google.code.pokerstats_service.PlayerResult;
import com.google.code.pokerstats_service.PlayerStats;

/**
 * Calculates players' Nemesis and Sidekick: rival players whose participation
 * in a game causes the player to gain or lose the most.
 *
 * @author DL
 */
public class RivalsCalculator {
    /**
     * Constructor.
     * @param minGames   Minimum number of games to consider.
     */
    public RivalsCalculator(int minGames) {
        this.minGames = minGames;
    }
    
    public void updateStats(
	    Map<Game, Set<PlayerResult>> games, Map<Long, PlayerStats> playersStats) {
	
        // Players that played less than a minimum number of games cannot be
	// Nemesis or Sidekick
        Set<Long> smallTimePlayers = new HashSet<Long>();
        for (PlayerStats stats : playersStats.values()) {
            if (stats.gamesPlayed < minGames) {
                smallTimePlayers.add(stats.fbUid);
            }
        }

        // Create X-Y table of lists for regression analysis
        Map<Long, Map<Long, List<Double>>> xPlayerByRival =
            new HashMap<Long, Map<Long, List<Double>>>();
        Map<Long, Map<Long, List<Double>>> yPlayerByRival =
            new HashMap<Long, Map<Long, List<Double>>>();
        Map<Long, Map<Long, Double>> playerByRivalBetas =
            new HashMap<Long, Map<Long, Double>>();
        
        for (Long playerFbUid : playersStats.keySet()) {
            Map<Long, List<Double>> xByRival = new HashMap<Long, List<Double>>();
            Map<Long, List<Double>> yByRival = new HashMap<Long, List<Double>>();
            Map<Long, Double> betaByRival = new HashMap<Long, Double>();
            
            for (Long rivalFbUid : playersStats.keySet()) {
                if (rivalFbUid != playerFbUid) {
                    xByRival.put(rivalFbUid, new ArrayList<Double>());
                    yByRival.put(rivalFbUid, new ArrayList<Double>());
                    betaByRival.put(rivalFbUid, 0.0);
                }
            }

            xPlayerByRival.put(playerFbUid, xByRival);
            yPlayerByRival.put(playerFbUid, yByRival);
            playerByRivalBetas.put(playerFbUid, betaByRival);
        }

        // Populate table
        for (Game game : games.keySet()) {
            // Who participated in this game?
            Map<Long, Double> gamePlayers = new HashMap<Long, Double>();
            for (Long fbUid : playersStats.keySet()) {
                gamePlayers.put(fbUid, 0.0);
            }
            for (PlayerResult playerResult : games.get(game)) {
                gamePlayers.put(playerResult.player, 1.0);
            }

            // Get each player's profit
            for (PlayerResult playerResult : games.get(game)) {
                Double playerProfit = playerResult.cashOut - playerResult.buyIn;
                Map<Long, List<Double>> xByRival = xPlayerByRival.get(playerResult.player);
                Map<Long, List<Double>> yByRival = yPlayerByRival.get(playerResult.player);

                for (Long rivalFbUid : gamePlayers.keySet()) {
                    if (!rivalFbUid.equals(playerResult.player)) {
                        List<Double> xList = xByRival.get(rivalFbUid);
                        List<Double> yList = yByRival.get(rivalFbUid);

                        xList.add(gamePlayers.get(rivalFbUid));
                        yList.add(playerProfit);
                    }
                }
            }
        }

        // Analyze data
        for (Long playerFbUid : playerByRivalBetas.keySet()) {
            // Calculate correlations, find minimum and maximum
            PlayerStats playerStats = playersStats.get(playerFbUid);
            Map<Long, Double> betaByRival = playerByRivalBetas.get(playerFbUid);
            double maxCorr = 0;
            double minCorr = 0;

            for (Long rivalFbUid : betaByRival.keySet()) {
                double[] x = listToArray(xPlayerByRival.get(playerFbUid).get(rivalFbUid));
                double[] y = listToArray(yPlayerByRival.get(playerFbUid).get(rivalFbUid));

                double r = 0.0;
                try {
		    r = AnalysisUtility.sampleCorrelation(x, y);
		    if (Double.isNaN(r)) {
			r = 0.0;
		    }
		} catch (DataIsEmptyException e) {}
                betaByRival.put(rivalFbUid, r);

                if (!smallTimePlayers.contains(rivalFbUid)) {
                    if (r > 0) {
                        if (r >= maxCorr) {
                            maxCorr = r;
                            playerStats.sidekickFbUid = rivalFbUid;
                        }
                    } else if (r < 0) {
                        if (r < minCorr) {
                            minCorr = r;
                            playerStats.nemesisFbUid = rivalFbUid;
                        }
                    }
                }
            }
        }
    }

    private double[] listToArray(List<Double> list) {
        double[] result = new double[list.size()];
        int i = 0;
        for (Double val : list) {
            result[i++] = val;
        }
        return result;
    }
    
    /** Minimum number of games to consider. */
    private final int minGames;
}